<?php
namespace core\utils;


use core\filter\Filter;
use core\proxy\DaemonProxy;
use core\proxy\ProxyEnhancer;
use modules\home\model\UserModel;
use ReflectionClass;

class BeanManager
{
    private static $instance;

    private static $beans = [];

    private static $simpleBeans = [];

    public static $refClazz = [];

    // array[string]
    private static ?array $proxies = null;

    private function __construct() {}

    /**
     * @return BeanManager
     */
    public static function getInstance() : BeanManager
    {
        if (null == self::$instance)
            self::$instance = new self();

        return self::$instance;
    }

    /**
     * @param string $name  这里的name一定要是全路径，
     *                      就是带有命名空间的那种
     * @return ProxyEnhancer|false
     * @throws \ReflectionException
     */
    public function getBean(string $name) {
        if (!isset(self::$beans[$name])) {
            $ref = new \ReflectionClass($name);
            $proxies = $this->matchProxies($ref);

            $enhances = [];
            for ($i = count($proxies) - 1; $i >= 0; $i --) {
                $enhances[$i] = new ProxyEnhancer($enhances[$i + 1] ?? null, $ref, $proxies[$i], $name);
            }
            $rev = new ProxyEnhancer(null, $ref, null, $name);
            if (count($enhances) > 0) {
                $enhances[count($enhances) - 1]->setNext($rev);
            }
            return self::$beans[$name] = $enhances[0] ?? $rev;
        } else {
            return self::$beans[$name];
        }
    }

    public function destroyBean(string $name) {
        unset(self::$beans[$name]);
    }

    private function matchProxies(ReflectionClass $class) {
        if ($class->isSubclassOf(DaemonProxy::class)) {
            return [];
        }
        // 命中的Proxies
        $matchProxies = [];
        if (null !== self::$proxies) {
            foreach (self::$proxies as $proxyClass) {
                // 这里依赖getBean来实现参数注入
                // 但是proxy的实例并不是单例模式的
                $proxy = BeanManager::getInstance()->getBean($proxyClass);

                // 是Proxy类的子类并且定义命中请求
                if ($proxy instanceof DaemonProxy && $proxy->matchClass($class)) {
                    $matchProxies[] = $proxy;
                }

                $this->destroyBean($proxyClass);
            }
        } else {
            // 获取所有定义的Filters(文件名数组)
            $proxyFiles = $this->readAllDeclareProxy();

            self::$proxies = [];
            foreach ($proxyFiles as $file) {
                // 实例化Filter数组
                $proxyClass = str_replace('/', '\\', str_replace('.php', '', $file));
                if (!isset(self::$refClazz[$proxyClass])) {
                    try {
                        self::$refClazz[$proxyClass] = new ReflectionClass($proxyClass);
                    } catch (\ReflectionException $ignore) {}
                }

                if (!self::$refClazz[$proxyClass]->isSubclassOf(DaemonProxy::class)) {
                    continue;
                }

                self::$proxies[] = $proxyClass;

                // 这里依赖getBean来实现参数注入
                // 但是proxy的实例并不是单例模式的
                $proxy = BeanManager::getInstance()->getBean($proxyClass);

                // 是Proxy类的子类并且定义命中请求
                if ($proxy instanceof DaemonProxy && $proxy->matchClass($class)) {
                    $matchProxies[] = $proxy;
                }

                $this->destroyBean($proxyClass);
            }
        }
        // 根据Filter的sort字段对Filters排序
        usort($matchProxies, function ($a,  $b) { try {return $a->getSort() - $b->getSort(); }catch (\Exception $e) {} finally{}return 0;});

        return $matchProxies;
    }



    /**
     * 读取所有的定义的Filter
     * @return array
     */
    private function readAllDeclareProxy() {
        $proxies = [];
        $proxyDir = config('proxy.dir');
        foreach ($proxyDir as $dir) {
            if (is_dir($dir)) {
                $proxies += $this->getProxiesRecursion($dir);
            } else {
                throw new Error('无法找到filter对应的文件夹');
            }
        }
        return $proxies;
    }

    /**
     * 递归的读取目录下的所有Filter
     * @param $dir
     * @return array
     */
    private function getProxiesRecursion(string $dir) : array {
        $handler = opendir($dir);
        $rv = [];
        while ($file = readdir($handler)) {
            if ('.' == $file || '..' == $file) { // 忽略当前和父目录
                continue;
            } else if (is_dir($dir . '/' . $file)) { // 如果是路径那就递归读取
                $rv += $this->getProxiesRecursion($dir . '/' . $file);
            } else { // 如果是文件名并且以php作为最终后缀的
                if (substr($file, strrpos($file, '.') + 1) == 'php')
                    $rv[] = $dir . '/' . $file;
            }
        }
        return $rv;
    }

    public function register(string $name, Bean $bean) {
        self::$beans[$name] = $bean;
    }
}